/******************************************************************
 *
 *    Java Lib For JavaWeb, Powered By Zhejiang hangzhou.
 *
 *    Copyright (c) 2001-2020
 *    http://tankpao.tk/
 *
 *    Package:     tk.tankpao.blast.core
 *
 *    Filename:    ApplicationContext.java
 *
 *    Description: TODO(用一句话描述该文件做什么)
 *
 *    Copyright:   Copyright (c) 2001-2020
 *
 *    Company:     tankpao.tk
 *
 *    @author:     王志刚
 *
 *    @version:    1.0.0
 *
 *    Create at:   2017年2月9日 下午2:45:21
 *
 *    Revision:
 *
 *    2017年2月9日 下午2:45:21
 *        - first revision
 *
 *****************************************************************/
package tk.tankpao.blast.core;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import tk.tankpao.blast.constant.ApplicationConstant;
import tk.tankpao.blast.core.annotation.Aspect;
import tk.tankpao.blast.core.annotation.Autowired;
import tk.tankpao.blast.core.annotation.Component;
import tk.tankpao.blast.core.annotation.Pointcut;
import tk.tankpao.blast.core.aop.AopBaseActor;
import tk.tankpao.blast.util.PackageScanner;
import tk.tankpao.blast.util.PropertiesLoader;
import tk.tankpao.blast.util.StringUtils;

/**
 * 
 * @ClassName ApplicationContext
 * @Description blast核心类
 * @author 王志刚
 * @Date 2017年2月9日 下午2:45:33
 * @version 1.0.0
 */
public class ApplicationContext {
	
	private static String[] packageDirs;
	private static Properties pps;
	
	@SuppressWarnings("rawtypes")
	private static Map<String, Class> classeMap = new HashMap<String, Class>();
	@SuppressWarnings("rawtypes")
	private static Map<String, Class> aspectMap = new HashMap<String, Class>();
	private static ConcurrentHashMap<String, Object> objectMap = new ConcurrentHashMap<String, Object>();

	public static void init(String contextConfigLocation) {
		ApplicationContext.pps = PropertiesLoader.loadProperties(contextConfigLocation);
		ApplicationContext.start(ApplicationContext.pps.get(ApplicationConstant.BASE_PACKAGE).toString());
	}
	
	public static void start(String packageDirStr) {
		ApplicationContext.packageDirs = packageDirStr.replaceAll(" +", "").split(",");
		for(String packageDir : packageDirs){
			loadClassMapping(packageDir);
		}
		AopBaseActor.init(aspectMap);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void loadClassMapping(String packageDir) {
		Set<Class> classes = PackageScanner.getClasses(packageDir);
		classes.forEach(clazz -> {
			if (clazz.isAnnotationPresent(Component.class)) {
				Component componentAn = (Component) clazz.getAnnotation(Component.class);
				classeMap.put(componentAn.name(), clazz);
			}
			if (clazz.isAnnotationPresent(Aspect.class)) {
				if(clazz.isAnnotationPresent(Pointcut.class)){
					Pointcut pointcut = (Pointcut) clazz.getAnnotation(Pointcut.class);
					if(!StringUtils.isEmpty(pointcut.path())){
						aspectMap.put(pointcut.path().trim(), clazz);
					}
				}
			}
		});
	}

	@SuppressWarnings("rawtypes")
	public static Object getBean(String beanName) throws Exception {
		Class clazz = classeMap.get(beanName);
		Object bean = null;
		if (clazz == null) {
			return null;
		}
		if(objectMap.get(beanName) == null){
			bean = Class.forName(clazz.getName()).newInstance();
			
			Field[] fields = clazz.getDeclaredFields();
			for(Field field : fields){
				if(field.isAnnotationPresent(Autowired.class)){
					Autowired auto = field.getAnnotation(Autowired.class);
					String inBeanName = auto.name();
					field.setAccessible(true);
					field.set(bean, getBean(inBeanName, field.getType()));
				}
			}
			
			bean = AopBaseActor.enhanceObject(bean, clazz);
			
			objectMap.put(beanName, bean);
		}else{
			bean = objectMap.get(beanName);
		}
		return bean;
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String beanName, Class<T> requiredType) throws Exception {
		Object bean = getBean(beanName);
		if (requiredType != null && !requiredType.isAssignableFrom(bean.getClass())) {
			throw new Exception("类型错误"+requiredType.getName());
		}
		return (T) bean;
	}

	@SuppressWarnings("rawtypes")
	public static Map<String, Class> getClasseMap() {
		return classeMap;
	}

	public static void setClasseMap(Map<String, Class> classeMap) {
		ApplicationContext.classeMap = classeMap;
	}

	public static ConcurrentHashMap<String, Object> getObjectMap() {
		return objectMap;
	}

	public static void setObjectMap(ConcurrentHashMap<String, Object> objectMap) {
		ApplicationContext.objectMap = objectMap;
	}

	public static String[] getPackageDirs() {
		return packageDirs;
	}

	public static void setPackageDirs(String[] packageDirs) {
		ApplicationContext.packageDirs = packageDirs;
	}

	public static Properties getPps() {
		return pps;
	}

	public static void setPps(Properties pps) {
		ApplicationContext.pps = pps;
	}
}
